#!/bin/bash

# 部署脚本 - 支持上线和下线操作
# 使用方式: ./deploy.sh on|off

LOG_FILE="deploy.log"

# 日志记录函数
log() {
    local message="$1"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$timestamp] $message" | tee -a "$LOG_FILE"
}

# 错误处理函数
error_exit() {
    local message="$1"
    log "ERROR: $message"
    exit 1
}

# 获取当前分支名
get_current_branch() {
    git branch --show-current 2>/dev/null || git rev-parse --abbrev-ref HEAD
}

# 获取主分支名（master或main）
get_main_branch() {
    # 检查是否存在main分支
    if git show-ref --verify --quiet refs/heads/main; then
        echo "main"
    elif git show-ref --verify --quiet refs/heads/master; then
        echo "master"
    else
        error_exit "未找到main或master分支"
    fi
}

# 确认用户操作
confirm() {
    local message="$1"
    echo "$message"
    read -p "请输入 y 确认，其他任意键取消: " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        log "用户取消操作"
        exit 0
    fi
}

# 原子分支切换部署
atomic_branch_deploy() {
    local main_branch="$1"
    local temp_branch="deploy-temp-$(date +%Y%m%d_%H%M%S)"
    
    log "开始原子分支切换部署"
    log "主分支: $main_branch"
    log "临时分支: $temp_branch"
    
    # 第一步：记录切换前 composer.lock 状态
    get_composer_lock_before
    
    # 第二步：创建临时分支并切换
    log "创建临时分支并切换: $temp_branch"
    if ! git checkout -b "$temp_branch"; then
        error_exit "创建临时分支失败"
    fi
    
    # 第二步：后台更新主分支（关键操作：不影响工作区）
    log "后台更新主分支代码（不影响当前工作区）"
    if ! git fetch origin "$main_branch:$main_branch"; then
        log "ERROR: 后台更新主分支失败"
        # 清理临时分支并回到原状态
        git checkout "$main_branch"
        git branch -D "$temp_branch" 2>/dev/null
        error_exit "后台更新主分支失败"
    fi
    
    # 第三步：原子切换到更新后的主分支
    log "原子切换到更新后的主分支"
    if ! git checkout "$main_branch"; then
        log "ERROR: 切换到主分支失败"
        # 尝试回到临时分支
        git checkout "$temp_branch" 2>/dev/null
        error_exit "切换到主分支失败，请手动检查状态"
    fi
    
    # 第四步：清理临时分支
    log "清理临时分支: $temp_branch"
    git branch -D "$temp_branch"
    
    log "原子分支切换部署完成"
}

# 获取 composer.lock 文件状态（在切换前调用）
get_composer_lock_before() {
    local composer_lock="composer.lock"
    
    if [[ -f "$composer_lock" ]]; then
        # 保存切换前的文件修改时间和MD5
        COMPOSER_LOCK_BEFORE_MTIME=$(stat -c %Y "$composer_lock" 2>/dev/null || stat -f %m "$composer_lock" 2>/dev/null)
        COMPOSER_LOCK_BEFORE_MD5=$(md5sum "$composer_lock" 2>/dev/null | cut -d' ' -f1 || md5 -q "$composer_lock" 2>/dev/null)
        log "记录切换前 composer.lock 状态: mtime=$COMPOSER_LOCK_BEFORE_MTIME, md5=${COMPOSER_LOCK_BEFORE_MD5:0:8}..."
    else
        COMPOSER_LOCK_BEFORE_MTIME=""
        COMPOSER_LOCK_BEFORE_MD5=""
        log "切换前未找到 composer.lock 文件"
    fi
}

# 检查并处理 Composer 依赖更新（在切换后调用）
check_and_update_composer() {
    local composer_lock="composer.lock"
    
    log "检查 Composer 依赖是否需要更新"
    
    # 检查 composer.lock 文件是否存在
    if [[ ! -f "$composer_lock" ]]; then
        log "未找到 composer.lock 文件，跳过依赖检查"
        return 0
    fi
    
    # 获取切换后的文件状态
    local after_mtime=$(stat -c %Y "$composer_lock" 2>/dev/null || stat -f %m "$composer_lock" 2>/dev/null)
    local after_md5=$(md5sum "$composer_lock" 2>/dev/null | cut -d' ' -f1 || md5 -q "$composer_lock" 2>/dev/null)
    
    log "切换后 composer.lock 状态: mtime=$after_mtime, md5=${after_md5:0:8}..."
    
    local need_update=false
    local update_reason=""
    
    # 情况1：切换前没有文件，切换后有文件
    if [[ -z "$COMPOSER_LOCK_BEFORE_MTIME" && -n "$after_mtime" ]]; then
        need_update=true
        update_reason="新增了 composer.lock 文件"
    # 情况2：切换前有文件，切换后没有文件
    elif [[ -n "$COMPOSER_LOCK_BEFORE_MTIME" && ! -f "$composer_lock" ]]; then
        log "composer.lock 文件被删除，跳过依赖检查"
        return 0
    # 情况3：切换前后都有文件，比较内容和时间
    elif [[ -n "$COMPOSER_LOCK_BEFORE_MTIME" && -n "$after_mtime" ]]; then
        # 比较MD5（内容是否变化）
        if [[ "$COMPOSER_LOCK_BEFORE_MD5" != "$after_md5" ]]; then
            need_update=true
            update_reason="composer.lock 文件内容发生变化"
        # 如果内容相同但时间不同，说明可能是文件时间戳变了但内容没变
        elif [[ "$COMPOSER_LOCK_BEFORE_MTIME" != "$after_mtime" ]]; then
            log "composer.lock 时间戳变化但内容未变，可能不需要重新安装依赖"
            need_update=false
        else
            log "composer.lock 文件未发生变化，跳过依赖安装"
            need_update=false
        fi
    fi
    
    # 如果需要更新依赖
    if [[ "$need_update" = true ]]; then
        echo "========================================="
        echo "⚠️  检测到 Composer 依赖变化"
        echo "原因: $update_reason"
        echo "切换前修改时间: $(format_timestamp "$COMPOSER_LOCK_BEFORE_MTIME")"
        echo "切换后修改时间: $(format_timestamp "$after_mtime")"
        echo "建议执行 composer install --no-dev 更新依赖"
        echo "========================================="
        
        # 询问用户是否执行 composer install
        echo "是否现在执行 composer install --no-dev？"
        read -p "请输入 y 执行，其他任意键跳过: " -n 1 -r
        echo
        
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            log "用户确认执行 composer install，原因: $update_reason"
            execute_composer_install
        else
            log "用户跳过 composer install"
            echo "========================================="
            echo "⚠️  已跳过 composer install"
            echo "请记得稍后手动执行: composer install --no-dev"
            echo "========================================="
        fi
    else
        log "composer.lock 文件无需更新依赖"
    fi
}

# 格式化时间戳显示
format_timestamp() {
    local timestamp="$1"
    if [[ -n "$timestamp" ]]; then
        date -d "@$timestamp" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date -r "$timestamp" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "未知时间"
    else
        echo "无"
    fi
}

# 执行 composer install
execute_composer_install() {
    log "开始执行 composer install --no-dev"
    
    # 检查 composer 命令是否存在
    if ! command -v composer >/dev/null 2>&1; then
        log "ERROR: composer 命令不存在"
        echo "========================================="
        echo "❌ composer 命令未找到"
        echo "请安装 Composer 或确保 composer 在 PATH 中"
        echo "手动执行: composer install --no-dev"
        echo "========================================="
        return 1
    fi
    
    # 执行 composer install
    echo "正在执行 composer install --no-dev ..."
    if composer install --no-dev --optimize-autoloader --no-interaction; then
        log "composer install 执行成功"
        echo "========================================="
        echo "✅ Composer 依赖安装完成"
        echo "========================================="
    else
        log "ERROR: composer install 执行失败"
        echo "========================================="
        echo "❌ Composer 依赖安装失败"
        echo "请手动执行: composer install --no-dev"
        echo "========================================="
        return 1
    fi
}

# 上线操作
deploy_on() {
    log "开始执行上线操作"
    
    # 检查是否在git仓库中
    if ! git rev-parse --git-dir > /dev/null 2>&1; then
        error_exit "当前目录不是git仓库"
    fi
    
    # 获取当前分支和主分支
    current_branch=$(get_current_branch)
    main_branch=$(get_main_branch)
    
    log "当前分支: $current_branch"
    log "主分支: $main_branch"
    
    # 检查当前分支是否为主分支
    if [[ "$current_branch" != "$main_branch" ]]; then
        log "WARNING: 当前不在主分支，可能有人回滚了代码"
        confirm "有人回滚了代码，不可以上线，请与他沟通。如果他确认了，请输入 y 继续:"
        
        log "开始更新主分支代码（回滚状态）"
        
        # 记录切换前状态
        get_composer_lock_before
        
        # 回滚状态：使用原子方式更新主分支
        log "后台更新主分支代码（不影响当前工作区）"
        git fetch origin "$main_branch:$main_branch" || error_exit "后台更新主分支失败"
        
        # 原子切换到更新后的主分支
        log "原子切换到更新后的主分支"
        git checkout "$main_branch" || error_exit "切换到主分支失败"
        
        log "主分支代码更新完成"
    else
        # 在主分支，使用原子分支切换方式更新
        log "在主分支，开始原子分支切换部署"
        
        # 检查工作区是否干净
        if ! git diff-index --quiet HEAD --; then
            error_exit "工作区有未提交的更改，请先提交或储藏"
        fi
        
        # 执行原子分支切换部署
        atomic_branch_deploy "$main_branch"
    fi
    
    # 检查 Composer 依赖更新
    check_and_update_composer
    
    log "上线操作执行完毕"
    echo "========================================="
    echo "✅ 服务器代码已更新，请重启后台常驻进程和 queue:listen"
    echo "========================================="
}

# 下线操作（回滚到上一个提交）
deploy_off() {
    log "开始执行下线操作"
    
    # 检查是否在git仓库中
    if ! git rev-parse --git-dir > /dev/null 2>&1; then
        error_exit "当前目录不是git仓库"
    fi
    
    # 获取当前提交信息
    current_commit=$(git rev-parse HEAD)
    current_message=$(git log -1 --pretty=format:"%s" HEAD)
    
    # 获取上一个提交信息
    prev_commit=$(git rev-parse HEAD~1)
    prev_message=$(git log -1 --pretty=format:"%s" HEAD~1)
    
    log "当前提交: $current_commit"
    log "当前提交信息: $current_message"
    log "上一个提交: $prev_commit"
    log "上一个提交信息: $prev_message"
    
    # 显示回滚信息并确认
    echo "========================================="
    echo "当前提交: $current_commit"
    echo "当前提交信息: $current_message"
    echo ""
    echo "即将回滚到上一个提交:"
    echo "提交ID: $prev_commit"
    echo "提交信息: $prev_message"
    echo "========================================="
    
    confirm "确认要回滚到上述提交吗？"
    
    log "用户确认回滚，开始执行回滚操作"
    
    # 检查工作区是否干净
    if ! git diff-index --quiet HEAD --; then
        log "WARNING: 工作区有未提交的更改"
        confirm "工作区有未提交的更改，继续回滚将丢失这些更改，确认继续吗？"
    fi
    
    # 记录回滚前 composer.lock 状态
    get_composer_lock_before
    
    # 执行回滚
    log "回滚到提交: $prev_commit"
    git checkout "$prev_commit" || error_exit "回滚失败"
    
    # 检查 Composer 依赖更新
    check_and_update_composer
    
    log "下线操作执行完毕"
    echo "========================================="
    echo "✅ 代码已回滚到上一个提交"
    echo "提交ID: $prev_commit"
    echo "提交信息: $prev_message"
    echo "请重启后台常驻进程和 queue:listen"
    echo "========================================="
}

# 显示帮助信息
show_help() {
    echo "用法: $0 [on|off]"
    echo ""
    echo "参数:"
    echo "  on   上线操作 - 拉取最新代码"
    echo "  off  下线操作 - 回滚到上一个提交"
    echo ""
    echo "示例:"
    echo "  $0 on   # 执行上线操作"
    echo "  $0 off  # 执行下线操作"
    echo ""
    echo "日志文件: $LOG_FILE"
}

# 主程序入口
main() {
    # 记录脚本开始执行
    log "========================================="
    log "部署脚本开始执行，参数: $*"
    log "执行用户: $(whoami)"
    log "执行目录: $(pwd)"
    log "========================================="
    
    # 检查参数
    if [[ $# -eq 0 ]]; then
        show_help
        exit 1
    fi
    
    case "$1" in
        "on")
            deploy_on
            ;;
        "off")
            deploy_off
            ;;
        "-h"|"--help"|"help")
            show_help
            ;;
        *)
            echo "错误: 未知参数 '$1'"
            show_help
            exit 1
            ;;
    esac
    
    log "========================================="
    log "部署脚本执行完成"
    log "========================================="
}

# 捕获中断信号
trap 'log "脚本被中断"; exit 130' INT TERM

# 执行主程序
main "$@"